use crate::protocol::type_schema::TypeSchema;
pub type DerivationTransform = (SubstitutionTransform, ConstructionTransform);

#[derive(Debug, Clone)]
pub enum SubstitutionTransform {
    Identity,
    Opt(Box<SubstitutionTransform>),
    List(Box<SubstitutionTransform>),
    Map(Box<SubstitutionTransform>, Box<SubstitutionTransform>),
    Tuple(Vec<SubstitutionTransform>),
    IntToFloat,
    FloatToInt,
    IntoOption(Box<SubstitutionTransform>), // unwrap an optional
}

#[derive(Debug, Clone)]
pub enum ConstructionTransform {
    Super,
    Void,
    Bool,
    Int,
    Float,
    Str,
    Json,
    Opt(Box<ConstructionTransform>),
    List(Box<ConstructionTransform>),
    Map(Box<ConstructionTransform>, Box<ConstructionTransform>),
    Tuple(Vec<ConstructionTransform>),
    DataType(String),
}

impl ConstructionTransform {
    pub fn resolve(&self, input_type: &TypeSchema) -> TypeSchema {
        match self {
            Self::Super => input_type.clone(),
            Self::Void => TypeSchema::Void,
            Self::Bool => TypeSchema::Bool,
            Self::Int => TypeSchema::Int,
            Self::Float => TypeSchema::Float,
            Self::Str => TypeSchema::Str,
            Self::Json => TypeSchema::Json,
            Self::Opt(inner) => TypeSchema::Opt(Box::new(inner.resolve(input_type))),
            Self::List(inner) => TypeSchema::List(Box::new(inner.resolve(input_type))),
            Self::Map(key, value) => TypeSchema::Map(Box::new(key.resolve(input_type)), Box::new(value.resolve(input_type))),
            Self::Tuple(inner) => {
                let mut result = vec![];
                for each in inner {
                    result.push(each.resolve(input_type));
                }
                TypeSchema::Tuple(result)
            }
            Self::DataType(name) => TypeSchema::DataType(name.clone()),
        }
    }

    pub fn is_super(&self) -> bool {
        match self {
            Self::Super => true,
            _ => false,
        }
    }
}

#[derive(Debug, Clone, Copy)]
pub enum FuseDirection {
    Equal,
    Positive, // 正向派生
    Negative, // 反向派生
}

impl FuseDirection {
    pub fn is_negative(&self) -> bool {
        match self {
            Self::Negative => true,
            Self::Equal => true,
            Self::Positive => false,
        }
    }
    pub fn is_positive(&self) -> bool {
        match self {
            Self::Positive => true,
            Self::Equal => true,
            Self::Negative => false,
        }
    }
}

#[derive(Debug, Clone, Copy)]
pub enum IOFixed {
    IO,
    I,
    O,
    Free,
}

impl IOFixed {
    pub fn from_bools(i: bool, o: bool) -> Self {
        match (i, o) {
            (true, true) => Self::IO,
            (true, false) => Self::I,
            (false, true) => Self::O,
            (false, false) => Self::Free,
        }
    }
}

